package eval;

import config.Config;
import network.Network;
import network.Port;
import network.Rule;
import networkLoader.FBNetwork;
import partition.SubspacePartition;
import verifier.JiffyVerifier;
import verifier.Ports.ArrayPorts;
import verifier.Ports.PersistentPorts;
import verifier.util.Changes;

import java.util.*;
import java.util.stream.Collectors;

public class LBEval {
    static Network network;

    public static void main(String[] args) {
        if (args.length == 2) {
            if (args[0].equals("0")) {
                Config.enableFMT = false;
            }
            if (args[1].equals("0")) {
                Config.enablePD = false;
            }
        }
        network = FBNetwork.getNetworkSrcHackMore();
//        SS(Config.enableFMT);
        seq(Config.enableFMT);
    }

    public static void seq(boolean useBatch) {
        System.out.println("useBatch: " + useBatch);
        JiffyVerifier verifier;
        if (Config.enablePD) {
            verifier = new JiffyVerifier(network, new PersistentPorts());
        } else {
            verifier = new JiffyVerifier(network, new ArrayPorts());

        }
        long s = System.nanoTime();
        if (useBatch) {
            s = System.nanoTime();
            Changes changes = verifier.insertMiniBatch(network.getInitialRules());
            verifier.update(changes);
            verifier.printTime(0);
        } else {
            for (Rule rule : network.getInitialRules()) {
                Changes changes = verifier.insertMiniBatch(new ArrayList<>(Collections.singletonList(rule)));
                verifier.update(changes);
            }
        }
        System.out.println("$seq: " + (System.nanoTime() - s));
        System.out.println("#EC: " + verifier.predSize());
    }

    public static void SS(boolean enableFFMT) {
        HashMap<Long, List<Rule>> ssToRules = new HashMap<>();
        for (Rule rule : network.getInitialRules()) {
            long ssId = (rule.getMatch().longValue() >> 16);
            ssToRules.computeIfAbsent(ssId, k -> new ArrayList<>());
            ssToRules.get(ssId).add(rule);
        }

        Set<Integer> ds = ssToRules.values().stream().map(List::size).collect(Collectors.toSet());
        System.out.println(ds.stream().max(Integer::compareTo));
        System.out.println(ds.stream().min(Integer::compareTo));

        int gccnt = 0;
        for (Map.Entry<Long, List<Rule>> entry : ssToRules.entrySet()) {
            System.out.println("Subspace: " + entry.getKey());
            SubspacePartition subspacePartition = new SubspacePartition(network, null, false);
            subspacePartition.run(entry.getValue(), enableFFMT);
//            if (gccnt++ % 100 == 0) System.gc();
        }
    }

    public static void LB(boolean enableFFMT) {
        HashMap<Long, List<Rule>> ssToRules = new HashMap<>();
        HashMap<Long, SubspacePartition> ssToSubspacePartition = new HashMap<>();
        for (Rule rule : network.getInitialRules()) {
            long ssId = (rule.getMatch().longValue() >> 16);
            ssToRules.computeIfAbsent(ssId, k -> new ArrayList<>());
            ssToRules.get(ssId).add(rule);
        }

        List<Thread> threads = new ArrayList<>();
        for (Map.Entry<Long, List<Rule>> entry : ssToRules.entrySet()) {
            SubspacePartition subspacePartition = new SubspacePartition(network, null, false);
            ssToSubspacePartition.put(entry.getKey(), subspacePartition);
            Thread thread = new Thread(subspacePartition);
            threads.add(thread);
        }

        for (Thread thread : threads) {
            thread.start();
        }

        for (Map.Entry<Long, List<Rule>> entry : ssToRules.entrySet()) {
            ssToSubspacePartition.get(entry.getKey()).getPendingBatches().addAll(entry.getValue());

        }
    }
}
